#if !defined(DATA_MANAGER_H_)
#define DATA_MANAGER_H_

#include <array>
#include <string>
#include <thread>

#include "data_scene.h"

namespace autodrive {
namespace perception {
class DataManager final {
 public:
  static constexpr std::int32_t kMaxDataSceneNumber{16};
  static constexpr std::int32_t kMaxDataFrameNum{30};

  using DataSceneArray = fixed_array<DataScene, kMaxDataSceneNumber>;
  using DataSceneArrayPtr = std::shared_ptr<DataSceneArray>;
  using DataScenePtr = DataScene *;

  using DataFrameArray = std::array<DataScene::DataFrame, kMaxDataFrameNum>;
  using DataFrameArrayPtr = std::shared_ptr<DataFrameArray>;

 public:
  /// @brief data manager settings
  struct DataManagerSettings {
    /// @brief data scene path
    std::vector<std::string> data_scene_paths;
    /// @brief load data frame settings
    DataScene::LoadDataFrameSettings load_data_frame_settings;
    /// @brief loading cycle
    std::int32_t cycle;
  };

 public:
  DataManager(DataManager &&) = delete;
  DataManager(const DataManager &) = delete;
  DataManager &operator=(DataManager &&) = delete;
  DataManager &operator=(const DataManager &) = delete;
  ~DataManager();

 public:
  ///@brief Get instance
  ///@return service instance
  static DataManager &GetInstance() noexcept;

  ///@brief init
  ///@param settings DataManagerSettings
  void Init(DataManagerSettings const &settings) noexcept;

  /// @brief load all dataframe circlely
  void Load() noexcept(false);

  /// @brief close
  void Close() noexcept;

  /// @brief
  /// @return
  std::pair<DataScene::DataFrame &, DataScene &> LastestDataFrame() const
      noexcept(false);

  /// @brief
  /// @return
  inline bool IsInit() const noexcept { return is_init_; }

  /// @brief
  /// @return
  inline bool IsLoading() const noexcept { return is_loading_; }

 protected:
  DataManager();

  /// @brief sleep for millisecond
  /// @param milliseconds
  void msleep(std::uint32_t milliseconds) noexcept {
    std::chrono::milliseconds dura{milliseconds};
    std::this_thread::sleep_for(dura);
  }
  /// @brief show map info information
  void show_info() noexcept;

 private:
  /// @brief init flag
  bool is_init_;
  /// @brief  init call once flag
  std::once_flag init_flag_;
  /// @brief data manager settings
  DataManagerSettings settings_;
  /// @brief data frames pointer
  DataFrameArrayPtr data_frames_ptr_;
  /// @brief is loading flag
  bool is_loading_;
  /// @brief mutex
  std::mutex mutex_;
  /// @brief load thread
  std::thread thread_load_;
  /// @brief data scene array
  DataSceneArrayPtr data_scene_array_ptr_;
  /// @brief latest data frame index;
  std::int32_t lastest_data_frame_index_;
  /// @brief latest data scene
  DataScenePtr latest_data_scene_ptr_;
  /// @brief is dispose flag
  bool is_dispose_;
};

}  // namespace perception
}  // namespace autodrive

#endif  // DATA_MANAGER_H_
